package com.tsfyun.scm.service.impl.finance;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.tsfyun.common.base.dto.CostWriteOffDTO;
import com.tsfyun.common.base.dto.WriteOffOrderCostDTO;
import com.tsfyun.common.base.dto.WriteOffOrderCostPlusDTO;
import com.tsfyun.common.base.enums.DistributedLockEnum;
import com.tsfyun.common.base.enums.finance.WriteOffTypeEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.common.base.util.TsfPreconditions;
import com.tsfyun.scm.base.DataCalling;
import com.tsfyun.scm.config.declare.DeclareNoticeSender;
import com.tsfyun.scm.config.declare.ProducerRabbitConfig;
import com.tsfyun.scm.entity.finance.*;
import com.tsfyun.scm.entity.order.ExpOrder;
import com.tsfyun.scm.entity.order.ExpOrderCost;
import com.tsfyun.scm.entity.order.ImpOrder;
import com.tsfyun.scm.entity.order.ImpOrderCost;
import com.tsfyun.scm.service.finance.*;
import com.tsfyun.scm.service.order.IExpOrderCostService;
import com.tsfyun.scm.service.order.IExpOrderService;
import com.tsfyun.scm.service.order.IImpOrderCostService;
import com.tsfyun.scm.service.order.IImpOrderService;
import com.tsfyun.scm.vo.order.ExpOrderCostVO;
import com.tsfyun.scm.vo.order.ImpOrderCostVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

@RefreshScope
@Service
@Slf4j
public class WriteOffServiceImpl implements IWriteOffService {

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @Value("${redis.lock.time:5}")
    private Integer lockTime;

    @Autowired
    private IReceiptAccountService receiptAccountService;
    @Autowired
    private IImpOrderCostService impOrderCostService;
    @Autowired
    private IReceiptAccountMemberService receiptAccountMemberService;
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private IImpOrderService impOrderService;
    @Autowired
    private DeclareNoticeSender declareNoticeSender;
    @Autowired
    private IExpOrderCostService expOrderCostService;
    @Autowired
    private IExpOrderService expOrderService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void autoWriteOff(CostWriteOffDTO dto) {
        String lockKey = DistributedLockEnum.AUTO_COST_WRITE_OFF.getCode() + ":" + dto.getTenant() + ":" + dto.getCustomerId();
        Lock lock = redisLockRegistry.obtain(lockKey);
        boolean isLock;
        try {
            isLock = lock.tryLock(10, TimeUnit.SECONDS);
            if(!isLock) {
                log.error(String.format("未获取到锁，客户：【%s】",dto.getCustomerId()));
                throw new ServiceException("自动核销服务拥挤，请稍后再试");
            }
            //退款和代理报关是放在境内收款中
            //执行核销
            log.info(StrUtil.format("开始执行客户境内收款【{}】核销",dto.getCustomerId()));
            //获取客户可核销的收款单信息(进口核销)
            List<ReceiptAccount> receiptAccountList = receiptAccountService.canWriteOffList(dto.getCustomerId());
            if(CollUtil.isNotEmpty(receiptAccountList)){
                //核销进口应收费用
                for(ReceiptAccount receipt : receiptAccountList){
                    Boolean writeOffComplete = writeOffReceiptAccount(receipt,null);
                    //费用是否核销完成
                    if(writeOffComplete){
                        break;
                    }
                }
            }
            //获取客户可核销的收款单信息(出口核销)
            receiptAccountList = receiptAccountService.canWriteOffList(dto.getCustomerId());
            if(CollUtil.isNotEmpty(receiptAccountList)){
                //核销出口应收费用
                for(ReceiptAccount receipt : receiptAccountList){
                    Boolean writeOffComplete = writeOffReceiptAccountExp(receipt,null);
                    //费用是否核销完成
                    if(writeOffComplete){
                        break;
                    }
                }
            }

            // 核销代理报关应收信息
            receiptAccountList = receiptAccountList.stream().filter(ra-> ra.getAccountValue().compareTo(ra.getWriteValue()) == 1).collect(Collectors.toList());
            if(CollUtil.isEmpty(receiptAccountList)){return;}
            // 发送消息通知代理报关核销
            declareNoticeSender.send(ProducerRabbitConfig.NOTICE_EXCHANGE_NAME,ProducerRabbitConfig.DECLARE_SEND_WRITE_OFF, JSONObject.toJSONString(dto),Long.valueOf(15000));
        } catch (InterruptedException e) {
            log.error("自动核销获取锁异常",e);
            throw new ServiceException("自动核销服务拥挤，请稍后再试");
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void writeOffOrderId(ImpOrder impOrder) {
        String lockKey = DistributedLockEnum.IMP_ORDER_WRITE_OFF.getCode() + ":" + impOrder.getId();
        Lock lock = redisLockRegistry.obtain(lockKey);
        boolean isLock;
        try {
            isLock = lock.tryLock(10, TimeUnit.SECONDS);
            if(!isLock) {
                log.error(String.format("根据订单核销未获取到锁，订单：【%s】",impOrder.getId()));
                throw new ServiceException("核销服务拥挤，请稍后再试");
            }
            //获取客户可核销的收款单信息
            List<ReceiptAccount> receiptAccountList = receiptAccountService.canWriteOffList(impOrder.getCustomerId());
            if(CollUtil.isEmpty(receiptAccountList)){return;}
            for(ReceiptAccount receipt : receiptAccountList){
                Boolean writeOffComplete = writeOffReceiptAccount(receipt,impOrder.getId());
                //费用是否核销完成
                if(writeOffComplete){
                    break;
                }
            }

        } catch (InterruptedException e) {
            log.error("订单核销获取锁异常",e);
            throw new ServiceException("核销服务拥挤，请稍后再试");
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void writeOffExpOrderId(ExpOrder expOrder) {
        String lockKey = DistributedLockEnum.EXP_ORDER_WRITE_OFF.getCode() + ":" + expOrder.getId();
        Lock lock = redisLockRegistry.obtain(lockKey);
        boolean isLock;
        try {
            isLock = lock.tryLock(10, TimeUnit.SECONDS);
            if(!isLock) {
                log.error(String.format("根据出口订单核销未获取到锁，订单：【%s】",expOrder.getId()));
                throw new ServiceException("核销服务拥挤，请稍后再试");
            }
            //获取客户可核销的收款单信息
            List<ReceiptAccount> receiptAccountList = receiptAccountService.canWriteOffList(expOrder.getCustomerId());
            if(CollUtil.isEmpty(receiptAccountList)){return;}
            for(ReceiptAccount receipt : receiptAccountList){
                Boolean writeOffComplete = writeOffReceiptAccountExp(receipt,expOrder.getId());
                //费用是否核销完成
                if(writeOffComplete){
                    break;
                }
            }
        } catch (InterruptedException e) {
            log.error("出口订单核销获取锁异常",e);
            throw new ServiceException("核销服务拥挤，请稍后再试");
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void writeOffRefundAccount(RefundAccount refundAccount) {
        //获取客户可核销的收款单信息
        List<ReceiptAccount> receiptAccountList = receiptAccountService.canWriteOffList(refundAccount.getCustomerId());
        TsfPreconditions.checkArgument(CollUtil.isNotEmpty(receiptAccountList),new ServiceException("收款单无可退款金额"));
        //本次退款申请金额即需要核销的金额（剩余需要核销的金额）
        BigDecimal accountValue = refundAccount.getAccountValue();
        for(ReceiptAccount receiptAccount : receiptAccountList) {
            //收款单可核销金额=收款金额-已核销金额
            BigDecimal receiptValue = StringUtils.rounded2(receiptAccount.getAccountValue().subtract(receiptAccount.getWriteValue()));
            if(receiptValue.compareTo(BigDecimal.ZERO) == 1) {
                //本次核销金额
                BigDecimal thisReceipt = BigDecimal.ZERO;
                if(receiptValue.compareTo(accountValue) >= 0) {
                    //全部核销
                    thisReceipt = accountValue;
                    accountValue = BigDecimal.ZERO;
                } else {
                    //部分核销
                    thisReceipt = receiptValue;
                    accountValue = StringUtils.rounded2(accountValue.subtract(receiptValue));
                }
                //修改收款单已核销金额
                receiptAccountService.writeValue(receiptAccount.getId(),thisReceipt);
                //增加收款单明细
                ReceiptAccountMember receiptAccountMember = new ReceiptAccountMember();
                receiptAccountMember.setReceiptAccountId(receiptAccount.getId());
                receiptAccountMember.setRefundAccountId(refundAccount.getId());
                receiptAccountMember.setDocNo(receiptAccount.getDocNo());
                receiptAccountMember.setAccountValue(thisReceipt);
                receiptAccountMember.setExpenseSubjectId("A0034");
                receiptAccountMember.setExpenseSubjectName("退款");
                receiptAccountMember.setWriteOffType(WriteOffTypeEnum.REFUND.getCode());
                receiptAccountMember.setOperation("系统");
                receiptAccountMemberService.saveNonNull(receiptAccountMember);
                if(accountValue.compareTo(BigDecimal.ZERO) == 0) {
                    log.info("退款单【{}】核销完毕",refundAccount.getDocNo());
                    break;
                }
            } else {
                log.info("收款单【{}】可核销金额小于等于0",receiptAccount.getDocNo());
            }
        }
        if(accountValue.compareTo(BigDecimal.ZERO) != 0 ){
            throw new ServiceException("可退款金额不足");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void writeOffOrderId(Long orderId) {
        writeOffOrderId(impOrderService.getById(orderId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void writeOffExpOrderId(Long orderId) {
        writeOffExpOrderId(expOrderService.getById(orderId));
    }
    // 进口核销
    @Transactional(rollbackFor = Exception.class)
    public Boolean writeOffReceiptAccount(ReceiptAccount receiptAccount,Long orderId){
        //收款单可核销金额
        BigDecimal receiptVal = receiptAccount.getAccountValue().subtract(receiptAccount.getWriteValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
        if(receiptVal.compareTo(BigDecimal.ZERO)!=1){return Boolean.FALSE;}
        //查询客户订单需要核销的费用
        List<ImpOrderCostVO> orderCostList = null;
        if(Objects.nonNull(orderId)){//根据订单核销
            orderCostList = impOrderCostService.canWriteOffByOrderId(orderId);
        }else{//根据客户核销
            orderCostList = impOrderCostService.canWriteOffList(receiptAccount.getCustomerId());
        }
        if(CollUtil.isEmpty(orderCostList)){return Boolean.TRUE;}
        List<ReceiptAccountMember> ramList = Lists.newArrayList();
        for (ImpOrderCostVO orderCost : orderCostList){
            //订单费用需要核销的金额
            BigDecimal costVal = orderCost.getReceAmount().subtract(orderCost.getAcceAmount()).setScale(2,BigDecimal.ROUND_HALF_UP);
            if(costVal.compareTo(BigDecimal.ZERO)!=1){
                //核销完成进入下一个
                continue;
            }
            //本次核销金额
            BigDecimal outwriteValue = BigDecimal.ZERO;
            if(receiptVal.compareTo(costVal)>=0){
                //核销订单全部费用
                outwriteValue = costVal;
                receiptVal = receiptVal.subtract(costVal).setScale(2,BigDecimal.ROUND_HALF_UP);
            }else{
                //核销订单部分费用
                outwriteValue = receiptVal;
                receiptVal = BigDecimal.ZERO;
            }
            //收款单核销金额
            receiptAccount.setWriteValue(receiptAccount.getWriteValue().add(outwriteValue).setScale(2,BigDecimal.ROUND_HALF_UP));

            //订单费用核销金额
            ImpOrderCost updateCost = new ImpOrderCost();
            updateCost.setId(orderCost.getId());
            updateCost.setAcceAmount(orderCost.getAcceAmount().add(outwriteValue).setScale(2,BigDecimal.ROUND_HALF_UP));
            impOrderCostService.updateByIdSelective(updateCost);

            ReceiptAccountMember ram = new ReceiptAccountMember();
            ram.setId(snowflake.nextId());
            ram.setReceiptAccountId(receiptAccount.getId());
            ram.setImpOrderCostId(orderCost.getId());
            ram.setDocNo(orderCost.getImpOrderNo());
            ram.setExpenseSubjectId(orderCost.getExpenseSubjectId());
            ram.setExpenseSubjectName(orderCost.getExpenseSubjectName());
            ram.setAccountValue(outwriteValue);
            ram.setWriteOffType(WriteOffTypeEnum.RECEIVABLE.getCode());
            ram.setOperation("系统");
            ram.setDateCreated(LocalDateTime.now());
            ramList.add(ram);
            if(receiptVal.compareTo(BigDecimal.ZERO)!=1){
                break;
            }
        }
        if(CollUtil.isNotEmpty(ramList)){
            receiptAccountMemberService.savaBatch(ramList);
        }
        ReceiptAccount updateReceipt = new ReceiptAccount();
        updateReceipt.setId(receiptAccount.getId());
        updateReceipt.setWriteValue(receiptAccount.getWriteValue());
        receiptAccountService.updateByIdSelective(updateReceipt);
        return Boolean.FALSE;
    }
    // 出口核销
    @Transactional(rollbackFor = Exception.class)
    public Boolean  writeOffReceiptAccountExp(ReceiptAccount receiptAccount,Long orderId){
        //收款单可核销金额
        BigDecimal receiptVal = receiptAccount.getAccountValue().subtract(receiptAccount.getWriteValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
        if(receiptVal.compareTo(BigDecimal.ZERO)!=1){return Boolean.FALSE;}
        //查询客户订单需要核销的费用
        List<ExpOrderCostVO> orderCostList = null;
        if(Objects.nonNull(orderId)){//根据订单核销
            orderCostList = expOrderCostService.canWriteOffByOrderId(orderId);
        }else{//根据客户核销
            orderCostList = expOrderCostService.canWriteOffList(receiptAccount.getCustomerId());
        }
        if(CollUtil.isEmpty(orderCostList)){return Boolean.TRUE;}
        List<ReceiptAccountMember> ramList = Lists.newArrayList();
        for (ExpOrderCostVO orderCost : orderCostList){
            //订单费用需要核销的金额
            BigDecimal costVal = orderCost.getReceAmount().subtract(orderCost.getAcceAmount()).setScale(2,BigDecimal.ROUND_HALF_UP);
            if(costVal.compareTo(BigDecimal.ZERO)!=1){
                //核销完成进入下一个
                continue;
            }
            //本次核销金额
            BigDecimal outwriteValue = BigDecimal.ZERO;
            if(receiptVal.compareTo(costVal)>=0){
                //核销订单全部费用
                outwriteValue = costVal;
                receiptVal = receiptVal.subtract(costVal).setScale(2,BigDecimal.ROUND_HALF_UP);
            }else{
                //核销订单部分费用
                outwriteValue = receiptVal;
                receiptVal = BigDecimal.ZERO;
            }
            //收款单核销金额
            receiptAccount.setWriteValue(receiptAccount.getWriteValue().add(outwriteValue).setScale(2,BigDecimal.ROUND_HALF_UP));

            //订单费用核销金额
            ExpOrderCost updateCost = new ExpOrderCost();
            updateCost.setId(orderCost.getId());
            updateCost.setAcceAmount(orderCost.getAcceAmount().add(outwriteValue).setScale(2,BigDecimal.ROUND_HALF_UP));
            expOrderCostService.updateByIdSelective(updateCost);

            ReceiptAccountMember ram = new ReceiptAccountMember();
            ram.setId(snowflake.nextId());
            ram.setReceiptAccountId(receiptAccount.getId());
            ram.setExpOrderCostId(orderCost.getId());
            ram.setDocNo(orderCost.getExpOrderNo());
            ram.setExpenseSubjectId(orderCost.getExpenseSubjectId());
            ram.setExpenseSubjectName(orderCost.getExpenseSubjectName());
            ram.setAccountValue(outwriteValue);
            ram.setWriteOffType(WriteOffTypeEnum.RECEIVABLE_EXP.getCode());
            ram.setOperation("系统");
            ram.setDateCreated(LocalDateTime.now());
            ramList.add(ram);
            if(receiptVal.compareTo(BigDecimal.ZERO)!=1){
                break;
            }
        }
        if(CollUtil.isNotEmpty(ramList)){
            receiptAccountMemberService.savaBatch(ramList);
        }
        ReceiptAccount updateReceipt = new ReceiptAccount();
        updateReceipt.setId(receiptAccount.getId());
        updateReceipt.setWriteValue(receiptAccount.getWriteValue());
        receiptAccountService.updateByIdSelective(updateReceipt);
        return Boolean.FALSE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelWriteOffByImpOrderCost(ImpOrderCost impOrderCost) {
        List<ReceiptAccountMember> receiptAccountMembers = receiptAccountMemberService.getByImpOrderCostId(impOrderCost.getId());
        if(CollUtil.isNotEmpty(receiptAccountMembers)){
            List<Long> memberIds = Lists.newArrayList();
            Map<Long,BigDecimal> receiptMap = Maps.newLinkedHashMap();
            receiptAccountMembers.stream().forEach(ram ->{
                impOrderCost.setAcceAmount(impOrderCost.getAcceAmount().subtract(ram.getAccountValue()).setScale(2,BigDecimal.ROUND_HALF_UP));
                memberIds.add(ram.getId());
                BigDecimal cancelVal = receiptMap.get(ram.getReceiptAccountId());
                if(Objects.isNull(cancelVal)){
                    cancelVal = ram.getAccountValue();
                }else{
                    cancelVal = cancelVal.add(ram.getAccountValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
                }
                receiptMap.put(ram.getReceiptAccountId(),cancelVal);
            });
            if(impOrderCost.getAcceAmount().compareTo(BigDecimal.ZERO)!=0){
                throw new ServiceException("费用取消核销失败，原核销金额错误");
            }
            //修改订单费用
            impOrderCostService.updateById(impOrderCost);
            //删除核销明细
            receiptAccountMemberService.removeByIds(memberIds);
            //还原收款主单核销金额
            receiptMap.forEach((k,v)->{
                receiptAccountService.cancelWriteValue(k,v);
            });
        }else{
            if(impOrderCost.getAcceAmount().compareTo(BigDecimal.ZERO)==1){
                throw new ServiceException("未找到该笔应收对应的核销费用");
            }
        }
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void cancelWriteOffByExpOrderCost(ExpOrderCost expOrderCost) {
        List<ReceiptAccountMember> receiptAccountMembers = receiptAccountMemberService.getByExpOrderCostId(expOrderCost.getId());
        if(CollUtil.isNotEmpty(receiptAccountMembers)){
            List<Long> memberIds = Lists.newArrayList();
            Map<Long,BigDecimal> receiptMap = Maps.newLinkedHashMap();
            receiptAccountMembers.stream().forEach(ram ->{
                expOrderCost.setAcceAmount(expOrderCost.getAcceAmount().subtract(ram.getAccountValue()).setScale(2,BigDecimal.ROUND_HALF_UP));
                memberIds.add(ram.getId());
                BigDecimal cancelVal = receiptMap.get(ram.getReceiptAccountId());
                if(Objects.isNull(cancelVal)){
                    cancelVal = ram.getAccountValue();
                }else{
                    cancelVal = cancelVal.add(ram.getAccountValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
                }
                receiptMap.put(ram.getReceiptAccountId(),cancelVal);
            });
            if(expOrderCost.getAcceAmount().compareTo(BigDecimal.ZERO)!=0){
                throw new ServiceException("出口费用取消核销失败，原核销金额错误");
            }
            //修改订单费用
            expOrderCostService.updateById(expOrderCost);
            //删除核销明细
            receiptAccountMemberService.removeByIds(memberIds);
            //还原收款主单核销金额
            receiptMap.forEach((k,v)->{
                receiptAccountService.cancelWriteValue(k,v);
            });
        }else{
            if(expOrderCost.getAcceAmount().compareTo(BigDecimal.ZERO)==1){
                throw new ServiceException("未找到该笔应收对应的核销费用");
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
//    @GlobalTransactional(rollbackFor = Exception.class)
    public void cancelwriteOffByReceiptAccount(ReceiptAccount receiptAccount) {
        //找到收款单核销明细数据
        List<ReceiptAccountMember> receiptAccountMemberList = receiptAccountMemberService.getByReceiptAccountId(receiptAccount.getId());
        BigDecimal total = BigDecimal.ZERO;
        List<Long> memberIds = new ArrayList<>();
        // 代理报关费用需要取消核销的金额
        List<WriteOffOrderCostDTO> trustOrderCosts = new ArrayList<>();
        for(ReceiptAccountMember vo : receiptAccountMemberList){
            //核销退款单
            if(Objects.nonNull(vo.getRefundAccountId())){
                throw new ServiceException("当前收款单已经核销退款，请先作废退款单");
            }
            //核销进口订单费用
            if(Objects.nonNull(vo.getImpOrderCostId())){
                ImpOrderCost cost = impOrderCostService.getById(vo.getImpOrderCostId());
                if(Objects.isNull(cost)){
                    throw new ServiceException(String.format("单据：%s未找科目为：%s的费用信息",vo.getDocNo(),vo.getExpenseSubjectName()));
                }
                //修改核销金额
                cost.setAcceAmount(cost.getAcceAmount().subtract(vo.getAccountValue()).setScale(2,BigDecimal.ROUND_HALF_UP));
                if(cost.getAcceAmount().compareTo(BigDecimal.ZERO)==-1){
                    throw new ServiceException(String.format("单据：%s科目为：%s的费用信息反核销失败",vo.getDocNo(),vo.getExpenseSubjectName()));
                }
                //修改订单费用
                impOrderCostService.updateById(cost);
            }
            //核销出口订单费用
            if(Objects.nonNull(vo.getExpOrderCostId())){
                ExpOrderCost cost = expOrderCostService.getById(vo.getExpOrderCostId());
                if(Objects.isNull(cost)){
                    throw new ServiceException(String.format("单据：%s未找科目为：%s的费用信息",vo.getDocNo(),vo.getExpenseSubjectName()));
                }
                //修改核销金额
                cost.setAcceAmount(cost.getAcceAmount().subtract(vo.getAccountValue()).setScale(2,BigDecimal.ROUND_HALF_UP));
                if(cost.getAcceAmount().compareTo(BigDecimal.ZERO)==-1){
                    throw new ServiceException(String.format("单据：%s科目为：%s的费用信息反核销失败",vo.getDocNo(),vo.getExpenseSubjectName()));
                }
                //修改订单费用
                expOrderCostService.updateById(cost);
            }
            //核销代理报关费用
            if(Objects.nonNull(vo.getTrustOrderCostId())){
                trustOrderCosts.add(new WriteOffOrderCostDTO(vo.getTrustOrderCostId(),vo.getAccountValue()));
            }
            total = total.add(vo.getAccountValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
            receiptAccountService.cancelWriteValue(vo.getReceiptAccountId(),vo.getAccountValue());
            memberIds.add(vo.getId());
        }
        if(receiptAccount.getWriteValue().compareTo(total)!=0){
            throw new ServiceException("主单核销金额与明细核销金额不一致");
        }
        //删除核销明细
        receiptAccountMemberService.removeByIds(memberIds);

        if(CollUtil.isNotEmpty(trustOrderCosts)){
            // 需要取消代理报关费用核销
            DataCalling.cancelwriteOffByCosts(trustOrderCosts);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelwriteOffByRefundAccount(RefundAccount refundAccount) {
        //找到核销的收款单明细
        List<ReceiptAccountMember> receiptAccountMemberVOList = receiptAccountMemberService.getByRefundAccountId(refundAccount.getId());
        BigDecimal total = BigDecimal.ZERO;
        List<Long> memberIds = new ArrayList<>();
        for(ReceiptAccountMember vo : receiptAccountMemberVOList){
            total = total.add(vo.getAccountValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
            receiptAccountService.cancelWriteValue(vo.getReceiptAccountId(),vo.getAccountValue());
            memberIds.add(vo.getId());
        }
        if(refundAccount.getAccountValue().compareTo(total)!=0){
            throw new ServiceException("退款金额与取消核销金额不一致");
        }
        //删除核销明细
        receiptAccountMemberService.removeByIds(memberIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<WriteOffOrderCostDTO> writeOffTrustOrderCosts(WriteOffOrderCostPlusDTO dto) {
        //获取客户可核销的收款单信息
        List<ReceiptAccount> receiptAccountList = receiptAccountService.canWriteOffList(dto.getCustomerId());
        if(CollUtil.isEmpty(receiptAccountList)){return new ArrayList<>();}
        List<WriteOffOrderCostDTO> surplus = dto.getOrderCosts();
        if(CollUtil.isEmpty(surplus)){return new ArrayList<>();}
        for(ReceiptAccount receipt : receiptAccountList){
            Boolean writeOffComplete = writeOffReceiptAccountTrustOrderCosts(receipt,surplus);
            //费用是否核销完成
            if(writeOffComplete){
                break;
            }
        }
        return surplus.stream().filter(cost -> cost.getCurrentWriteAmount().compareTo(BigDecimal.ZERO) == 1).collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean writeOffReceiptAccountTrustOrderCosts(ReceiptAccount receiptAccount,List<WriteOffOrderCostDTO> surplus){
        List<WriteOffOrderCostDTO> syCosts = surplus.stream().filter(cost -> cost.getReceAmount().compareTo(cost.getAcceAmount()) == 1).collect(Collectors.toList());
        if(CollUtil.isEmpty(syCosts)){
            return Boolean.TRUE;
        }
        //收款单可核销金额
        BigDecimal receiptVal = receiptAccount.getAccountValue().subtract(receiptAccount.getWriteValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
        if(receiptVal.compareTo(BigDecimal.ZERO)!=1){return Boolean.FALSE;}
        List<ReceiptAccountMember> ramList = Lists.newArrayList();
        for (WriteOffOrderCostDTO orderCost : surplus){
            //订单费用需要核销的金额
            BigDecimal costVal = orderCost.getReceAmount().subtract(orderCost.getAcceAmount()).setScale(2,BigDecimal.ROUND_HALF_UP);
            if(costVal.compareTo(BigDecimal.ZERO)!=1){
                //核销完成进入下一个
                continue;
            }
            //本次核销金额
            BigDecimal outwriteValue = BigDecimal.ZERO;
            if(receiptVal.compareTo(costVal)>=0){
                //核销订单全部费用
                outwriteValue = costVal;
                receiptVal = receiptVal.subtract(costVal).setScale(2,BigDecimal.ROUND_HALF_UP);
            }else{
                //核销订单部分费用
                outwriteValue = receiptVal;
                receiptVal = BigDecimal.ZERO;
            }
            //收款单核销金额
            receiptAccount.setWriteValue(receiptAccount.getWriteValue().add(outwriteValue).setScale(2,BigDecimal.ROUND_HALF_UP));
            // 费用已收金额
            orderCost.setAcceAmount(orderCost.getAcceAmount().add(outwriteValue).setScale(2,BigDecimal.ROUND_HALF_UP));
            if(Objects.isNull(orderCost.getCurrentWriteAmount())){
                orderCost.setCurrentWriteAmount(BigDecimal.ZERO);
            }
            // 返回本次核销金额
            orderCost.setCurrentWriteAmount(orderCost.getCurrentWriteAmount().add(outwriteValue).setScale(2,BigDecimal.ROUND_HALF_UP));

            ReceiptAccountMember ram = new ReceiptAccountMember();
            ram.setId(snowflake.nextId());
            ram.setReceiptAccountId(receiptAccount.getId());
            ram.setTrustOrderCostId(orderCost.getId());
            ram.setDocNo(orderCost.getTrustOrderNo());
            ram.setExpenseSubjectId(orderCost.getExpenseSubjectId());
            ram.setExpenseSubjectName(orderCost.getExpenseSubjectName());
            ram.setAccountValue(outwriteValue);
            ram.setWriteOffType(WriteOffTypeEnum.DECLARE.getCode());
            ram.setOperation("系统");
            ram.setDateCreated(LocalDateTime.now());
            ramList.add(ram);
            if(receiptVal.compareTo(BigDecimal.ZERO)!=1){
                break;
            }
        }
        if(CollUtil.isNotEmpty(ramList)){
            receiptAccountMemberService.savaBatch(ramList);
        }
        ReceiptAccount updateReceipt = new ReceiptAccount();
        updateReceipt.setId(receiptAccount.getId());
        updateReceipt.setWriteValue(receiptAccount.getWriteValue());
        receiptAccountService.updateByIdSelective(updateReceipt);
        return Boolean.FALSE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelWriteOffByTrustOrderCost(WriteOffOrderCostDTO dto) {
        List<ReceiptAccountMember> receiptAccountMembers = receiptAccountMemberService.getByTrustOrderCostId(dto.getId());
        if(CollUtil.isNotEmpty(receiptAccountMembers)){
            List<Long> memberIds = Lists.newArrayList();
            Map<Long,BigDecimal> receiptMap = Maps.newLinkedHashMap();
            BigDecimal totalAccountValue = BigDecimal.ZERO;
            for(ReceiptAccountMember ram : receiptAccountMembers){
                memberIds.add(ram.getId());
                BigDecimal cancelVal = receiptMap.get(ram.getReceiptAccountId());
                if(Objects.isNull(cancelVal)){
                    cancelVal = ram.getAccountValue();
                }else{
                    cancelVal = cancelVal.add(ram.getAccountValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
                }
                receiptMap.put(ram.getReceiptAccountId(),cancelVal);

                totalAccountValue = totalAccountValue.add(ram.getAccountValue());
            }
            if(dto.getAcceAmount().compareTo(totalAccountValue)!=0){
                throw new ServiceException("费用取消核销失败，原核销金额错误");
            }
            //删除核销明细
            receiptAccountMemberService.removeByIds(memberIds);
            //还原收款主单核销金额
            receiptMap.forEach((k,v)->{
                receiptAccountService.cancelWriteValue(k,v);
            });
        }else{
            if(dto.getAcceAmount().compareTo(BigDecimal.ZERO)==1){
                throw new ServiceException("未找到该笔应收对应的核销费用");
            }
        }
    }

}
